/*
 * Glew.cpp
 *
 *  Created on: 28/03/2013
 *      Author: gonzalo
 */

#include "ProgramBuilder.h"
#include <GL/glew.h>
#include <glm/glm.hpp>

#include "../exceptions/all_exceptions.h"

#include <string>
#include <fstream>

std::vector<GLuint> ProgramBuilder::shaders;

ProgramBuilder::ProgramBuilder() {

}

ProgramBuilder::~ProgramBuilder() {

}

void ProgramBuilder::checkOpenglVersion() {
	int major, minor, revision;
	const GLubyte* sVersion = glGetString(GL_SHADING_LANGUAGE_VERSION_ARB);
	if (glGetError() == GL_INVALID_ENUM) {
		major = 1;
		minor = 0;
		revision = 51;
	}/* else {
	 std::cout << (char*) sVersion << std::endl;
	 }*/
}

void ProgramBuilder::clearShaderList() {
	shaders.clear();
}

void ProgramBuilder::loadShader(const char * shader_file, GLenum shaderType) {
	GLuint shader;
	std::string logstr;
	if (GLEW_ARB_shading_language_100 ) {
		shader = glCreateShader(shaderType);
		if (shader == 0) {
			// Throw
			throw ShaderError("glCreateShader:Error creating shader form file %s\n", shader_file);
		}

		// cargo el archivo de shaders en memoria
		std::ifstream v_shader_file(shader_file, std::ifstream::in);
		std::string v_str((std::istreambuf_iterator<char>(v_shader_file)),
				std::istreambuf_iterator<char>());
		const char* vs_code_array[] = { v_str.c_str() };

		glShaderSource(shader, 1, vs_code_array, NULL);

		// Compilar el shader
		glCompileShader(shader);

		// verificar resultado de la compilacion
		GLint vs_compilation_result;

		glGetShaderiv(shader, GL_COMPILE_STATUS, &vs_compilation_result);

		if (vs_compilation_result == GL_FALSE) {
			GLint logLen;
			glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &logLen);
			if (logLen > 0) {
				char * log = (char *) malloc(logLen);
				GLsizei written;
				glGetShaderInfoLog(shader, logLen, &written, log);
				logstr.assign(log);
				free(log);
				throw ShaderError("Vertex shader compilation failed in file %s\n log: %s\n",
						shader_file, logstr.c_str());
			}
			throw ShaderError("Vertex shader compilation failed in file %s\n\n", shader_file);
		}

		shaders.push_back(shader);
	}
}

GLuint ProgramBuilder::createProgram() {
	GLuint programHandle;
	std::string logstr;
	std::vector<GLuint>::iterator i;

	if (GLEW_ARB_shading_language_100 ) {

		if (shaders.size() == 0) {
			throw ProgramError("%s\n", "Error creating program object, there is no shader");
		}

		programHandle = glCreateProgram();
		if (programHandle == 0) {
			throw ProgramError("%s\n", "glCreateProgram: Error creating program object");
		}

		for (i = shaders.begin(); i != shaders.end(); i++) {
			glAttachShader(programHandle, *i);
		}

		shaders.clear();

		glLinkProgram(programHandle);

		GLint status;
		glGetProgramiv(programHandle, GL_LINK_STATUS, &status); // Error Check
		if (GL_FALSE == status) {
			GLint logLen;
			glGetProgramiv(programHandle, GL_INFO_LOG_LENGTH, &logLen);
			if (logLen > 0) {
				char * log = (char *) malloc(logLen);
				GLsizei written;
				glGetProgramInfoLog(programHandle, logLen, &written, log); // Get link Info Error
				logstr.assign(log);
				free(log);
				throw ProgramError("%s\n log:%s \n", "Failed to link shader program!",
						logstr.c_str());
			}
			throw ProgramError("%s\n", "Failed to link shader program!");
		}

		//glUseProgram(programHandle);

	}

	return programHandle;
}

